package org.newdawn.yapgame;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;

public class EventManager implements IEventManager
{
	protected LinkedList<Event> queue_current = new LinkedList<Event>();
	protected LinkedList<Event> queue_next = new LinkedList<Event>();
	
	protected HashMap<Class<? extends Event>, LinkedList<IEventHandler> > registered_events_map = null;
	
	public EventManager()
	{
		 registered_events_map = new HashMap<Class<? extends Event>, LinkedList<IEventHandler> >();
	}

	// ------------------------------------------------------------------------
	
	/**
	 * Internal method consolidates managed structure.
	 * For example all registered events with no handler assigned are removed
	 */
	protected void consolidate()
	{
		// unregister all not handled event types
		Iterator<Class<? extends Event>> ievent_cls = this.registered_events_map.keySet().iterator();
		while( ievent_cls.hasNext() )
		{
			Class<? extends Event> cls = ievent_cls.next();

			if( this.registered_events_map.get( cls ).size() == 0 )
			{
				ievent_cls.remove();
			}
		}
	}

	// ------------------------------------------------------------------------
	
	@Override
	public boolean registerEventHandler( Class<? extends Event> eventCls, IEventHandler handler )
	{
		if( handler == null ) return false;
		// register event class if not already done
		if( !registered_events_map.containsKey( eventCls ) )
		{
			registered_events_map.put( eventCls, new LinkedList<IEventHandler>() );
		}
		// add handler if not already registered
		LinkedList<IEventHandler> eventHandler = registered_events_map.get( eventCls );
		if( eventHandler.contains( handler ) ) return false;
		eventHandler.add( handler );
		return true;
	}

	// ------------------------------------------------------------------------
	
	@Override
	public boolean tick( long maxTimeout )
	{
		long time_max = SystemTimer.getTime()+maxTimeout;
		// switch "double-buffered" event queues
		queue_current.addAll( queue_next );
		queue_next.clear();
		
		Iterator<Event> ievent = queue_current.iterator();
		// iterate as long there are events and we don't timeout
		while( ievent.hasNext() && time_max > SystemTimer.getTime() )
		{
			Event e = ievent.next();
			// remove event from queue
			ievent.remove();
			// trigger event now
			trigger( e );
		}
		// return whether all events in queue are handled
		return (queue_current.size() == 0);
	}

	// ------------------------------------------------------------------------
	
	@Override
	public boolean enqueue( Event ev )
	{
		queue_next.add( ev );
		return true;
	}

	// ------------------------------------------------------------------------
	
	@Override
	public boolean trigger( Event e )
	{
		// find handler(s) and handle event
		Class<?> class_of_event = e.getClass(); 
		if( registered_events_map.containsKey( class_of_event ) )
		{
			Iterator<IEventHandler> ievent_handlers = registered_events_map.get( class_of_event ).iterator();
			while( ievent_handlers.hasNext() ) 
			{
				// stop event handling whenever event was consumed
				if( ievent_handlers.next().handleEvent( e ) ) return true;
			}
		}
		return false;
	}

	// ------------------------------------------------------------------------
	
	@Override
	public boolean unregisterEventHandler(IEventHandler handler) 
	{
		if( handler == null ) return false;
		Iterator< LinkedList<IEventHandler> > ihandler = this.registered_events_map.values().iterator();
		boolean removed_handler = false;
		while( ihandler.hasNext() )
		{
			Iterator<IEventHandler> ihandler_list = ihandler.next().iterator();
			while( ihandler_list.hasNext() )
			{
				if( ihandler_list.next() == handler )
				{
					ihandler_list.remove();
					removed_handler = true;
				}
			}
		}
		if( removed_handler ) consolidate();
		return removed_handler;
	}

	// ------------------------------------------------------------------------
	
	@Override
	public boolean unregisterEventHandler(Class<? extends Event> eventCls,
			IEventHandler handler) 
	{
		if( handler == null ) return false;
		if( !this.registered_events_map.containsKey( eventCls )  ) return false;
		
		Iterator< IEventHandler > ihandler_list = this.registered_events_map.get( eventCls ).iterator();
		while( ihandler_list.hasNext() )
		{
			if( ihandler_list.next() == handler )
			{
				ihandler_list.remove();
				consolidate();
				return true;
			}
		}
		return false;
	}

	// ------------------------------------------------------------------------
	
	@Override
	public long getNumHandlers() 
	{
		Iterator< Class<? extends Event> > ievent_types = this.registered_events_map.keySet().iterator();
		long sum = 0;
		while( ievent_types.hasNext() )
		{
			sum += this.registered_events_map.get( ievent_types.next() ).size();
		}
		return sum;
	}

	// ------------------------------------------------------------------------
	
	@Override
	public long getNumHandlers(Class<? extends Event> eventCls) 
	{
		if( !this.registered_events_map.containsKey( eventCls ) ) return 0;
		return this.registered_events_map.get( eventCls ).size();
	}

	// ------------------------------------------------------------------------
	
	@Override
	public long getNumRegisteredEventTypes() 
	{
		return this.registered_events_map.size();
	}
}
